package db

import (
	"fmt"
	"github.com/apache/iotdb-client-go/client"
	"github.com/wendy512/iec104/pkg/global"
	"github.com/wendy512/iec104/pkg/logger"
	"time"
)

type ReadCache struct {
	F32      map[string]float32
	DataTime time.Time
}

// GetF32Copy 获取最新缓存的副本(浅拷贝)
func (rc *ReadCache) GetF32Copy() ReadCache {
	copyCache := ReadCache{
		F32:      make(map[string]float32),
		DataTime: LatestCache.DataTime,
	}
	for k, v := range rc.F32 {
		copyCache.F32[k] = v
	}
	return copyCache
}

var LatestCache ReadCache

func IotDBInit() {
	for _, readConfig := range global.Config.Read {
		config := &client.PoolConfig{
			Host:     readConfig.Host,
			Port:     readConfig.Port,
			UserName: readConfig.UserName,
			Password: readConfig.Password,
		}
		readConfig.SessionPool = client.NewSessionPool(config, 3, 60000, 60000, false)

		// 读取假数据
		if !global.Config.Fake {
			checkIoTDB(readConfig)
		}
	}
}

func checkIoTDB(readConfig global.ReadConfig) {
	latest, t, err := ReadLatestF32(readConfig)
	if err != nil {
		logger.Fatalf("IoTDB", "%s check latest error: %v", readConfig.FieldHead, err)
	}
	logger.Infof("IoTDB", "%s check t:%v latest: %v", readConfig.FieldHead, t, latest)
}

func SetCacheFrom() {

	// 使用time.Tick创建一个每秒触发一次的定时器
	ticker := time.Tick(time.Duration(global.Config.ReadFreq) * time.Millisecond)

	for range ticker {
		var latest map[string]float32
		var latestAll map[string]float32
		var latestTime time.Time
		var err error
		for _, readConfig := range global.Config.Read {
			latest, latestTime, err = ReadLatestF32(readConfig)

			if err != nil || latest == nil {
				logger.Errorf("IoTDB", "%s SetCacheFrom read error: %v", readConfig.FieldHead, err)
				continue
			}

			for k, v := range latest {
				latestAll[k] = v
			}
		}

		//logger.Infof("IoTDB", "SetCacheFrom read t:%v latest: %v", t, latest)
		LatestCache = ReadCache{latestAll, latestTime}
	}
}

func ReadLatestF32(readConfig global.ReadConfig) (map[string]float32, time.Time, error) {

	// 读取假数据
	if global.Config.Fake {
		return ReadLatestF32Fake(readConfig)
	}

	var t time.Time

	session, err := readConfig.SessionPool.GetSession()
	defer readConfig.SessionPool.PutBack(session)
	if err != nil {
		return nil, t, err
		//logger.Fatalf("IoTDB", "get session error: %v", err)
	}

	var sqlFields string
	for idx, field := range readConfig.Fields {
		if idx == 0 {
			sqlFields += field.Name
		}
		if idx > 0 {
			sqlFields += ", " + field.Name
		}
	}

	// 修改：使用直接拼接的 sqlFields 替代 strings.Join
	sql := fmt.Sprintf("SELECT %s FROM %s ORDER BY TIME DESC LIMIT 1", sqlFields, readConfig.TimeSeries)

	sessionDataSet, err := session.ExecuteQueryStatement(sql, &readConfig.Timout)
	if err != nil {
		return nil, t, err
		//logger.Fatalf("IoTDB", "get ExecuteQueryStatement error: %v", err)
	}

	defer func(sessionDataSet *client.SessionDataSet) {
		_ = sessionDataSet.Close()
	}(sessionDataSet)

	result := make(map[string]float32)
	for next, errRead := sessionDataSet.Next(); errRead == nil && next; next, errRead = sessionDataSet.Next() {
		timestampMillis := sessionDataSet.GetInt64(client.TimestampColumnName)
		timestamp := timestampMillis / 1000
		nanos := (timestampMillis % 1000) * 1000000
		t = time.Unix(timestamp, nanos)

		for i := 0; i < sessionDataSet.GetColumnCount(); i++ {
			columnName := sessionDataSet.GetColumnName(i)

			switch sessionDataSet.GetColumnDataType(i) {
			case client.BOOLEAN:
				//result[columnName] = sessionDataSet.GetBool(columnName)
			case client.INT32:
				result[columnName] = float32(sessionDataSet.GetInt32(columnName))
			case client.INT64:
				result[columnName] = float32(sessionDataSet.GetInt64(columnName))
			case client.FLOAT:
				result[columnName] = sessionDataSet.GetFloat(columnName)
			case client.DOUBLE:
				result[columnName] = float32(sessionDataSet.GetDouble(columnName))
			case client.TEXT:
				//result[columnName] = float32(sessionDataSet.GetText(columnName))
			default:
			}
		}
	}

	return result, t, nil
}

var fakeData float32

func ReadLatestF32Fake(readConfig global.ReadConfig) (result map[string]float32, t time.Time, err error) {
	result = LatestCache.F32
	if result == nil {
		result = make(map[string]float32)
	}
	t = time.Now()
	err = nil

	fakeData += 1
	if fakeData > 100 {
		fakeData = 0
	}
	for idx, field := range readConfig.Fields {
		if idx == int(fakeData)%len(readConfig.Fields) {
			result[global.GetFieldKey(readConfig, field)] = fakeData
		}
	}

	logger.Infof("IoTDB", "read fake data t:%v result: %v", t, result)
	return
}
